package alp.starcode.nudtportal.framework.aspect;

import alp.starcode.nudtportal.framework.constant.ProjectConst;
import alp.starcode.nudtportal.framework.aspect.model.LogObject;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * servlet规范通用日志打印方法
 *
 * @author lastwhisper
 */
@Slf4j
@Aspect
@Component
@Profile("dev")
public class ControllerLogAop implements ProjectConst {

    /**
     * 切点
     */
    @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
    public void matchCondition() {
        if (log.isDebugEnabled()) {
            log.debug("AOP日志输出切点定义，{}", "io.swagger.annotations.ApiOperation");
        }
    }

    /**
     * 打印request和response
     */
    @Around("matchCondition()")
    public Object servletMethodProxy(ProceedingJoinPoint pjp) throws Throwable {
//            String userUuid = RequestHolder.getCurrentUser().getUserUuid();
        Instant startTime = Instant.now();
        // 前置处理
        LogObject logObject = getLogObject(pjp);
        StringBuilder sb = new StringBuilder();
        sb.append("操作 = ").append(logObject.getOperation());
        sb.append(", 请求路径 = ").append(logObject.getUrl());
        sb.append(", 请求方式 = ").append(logObject.getMethod());
//            sb.append(", 请求用户 = ").append(userUuid);
        if (logObject.getParameterMap().size() > 0) {
            sb.append(", 请求参数 = ").append(JSONObject.toJSONString(logObject.getParameterMap()));
        }
        String className = logObject.getClassName();
        sb.append(", 全限定名 = ").append(className);
        String methodName = logObject.getMethodName();
        sb.append(", 方法名称 = ").append(methodName);
        // 方法执行
        Object result = null;
        Instant endTime;
        try {
            result = pjp.proceed();
        } catch (Exception e) {
            endTime = Instant.now();
            sb.append(", 出现异常");
            sb.append(", 异常名称 = ").append(e.getClass().getName());
            sb.append(", 耗时 =  ").append(Duration.between(startTime, endTime).toMillis());
            sb.append("ms");
            log.error("{}", sb.toString());
            throw e;
        }
        // 后置处理
        endTime = Instant.now();
        sb.append(", 响应 = ").append(JSONObject.toJSON(result));
        sb.append(", 耗时 =  ").append(Duration.between(startTime, endTime).toMillis());
        sb.append("ms");
        if (log.isDebugEnabled()) {
            log.debug("{}", sb.toString());
        }
        return result;

    }


    public static LogObject getLogObject(JoinPoint point) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                .getRequest();
        LogObject logObject = new LogObject();
        logObject.setUrl(request.getRequestURI());
        logObject.setMethod(request.getMethod());
        logObject.setParameterMap(request.getParameterMap());
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        // 全限定名
        String className = methodSignature.getDeclaringType().getName();
        // 方法名
        String methodName = methodSignature.getName();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上面的注解
        ApiOperation trace = method.getAnnotation(ApiOperation.class);
        String operate;
        if (trace != null) {
            operate = trace.value();
        } else {
            operate = methodName;
        }
        Object[] args = point.getArgs();
        String[] parameterNames = methodSignature.getParameterNames();
        Map<String, Object> params = new HashMap<>();
        for (int i = 0; i < parameterNames.length; i++) {
            if (args[i] instanceof MultipartFile) {
                params.put(parameterNames[i], "二进制文件");
            } else if (args[i] instanceof HttpServletRequest) {
                params.put(parameterNames[i], "request");
            } else if (args[i] instanceof HttpServletResponse) {
                params.put(parameterNames[i], "response");
            } else {
                params.put(parameterNames[i], args[i]);
            }
        }
        logObject.setParameterMap(params);
        logObject.setClassName(className);
        logObject.setMethodName(methodName);
        logObject.setOperation(operate);
        return logObject;
    }

}
